﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AutoMock.Utilities;
using AutoMock.Frameworks;

namespace AutoMock
{
    internal class MocksCreator<T, TMockFramework>
    {
        public static Dictionary<Type, object> CreateMocks(IMockingFrameworkProvider<TMockFramework> framework, params object[] constructorDependencys)
        {
            var mocks = new Dictionary<Type, object>();

            var constructor = ReflectionHelper.GetConstructorWithMostParameters<T>();

            foreach (var parameter in constructor.GetParameters())
            {
                if(parameter.ParameterType.IsArray && !parameter.ParameterType.GetElementType().IsInterface || !parameter.ParameterType.IsArray && !parameter.ParameterType.IsInterface)
                    throw new Exception(string.Format("Parameter {0} is not an interface. Only interfaces are supported.", parameter.ParameterType.ToString()));

                if (mocks.ContainsKey(parameter.ParameterType))
                    continue;

                var injected = GetInstructorInjectedType(constructorDependencys, parameter);
                if (injected != null)
                {
                    mocks.Add(parameter.ParameterType, injected);
                    continue;
                }

                var mockInstance = framework.CreateStub(parameter.ParameterType.IsArray ? parameter.ParameterType.GetElementType(): parameter.ParameterType);

                Array instance = null;
                if (parameter.ParameterType.IsArray)
                {
                    instance = Array.CreateInstance(parameter.ParameterType.GetElementType(), 1);
                    instance.SetValue(mockInstance, 0);
                }
                

                mocks.Add(parameter.ParameterType, parameter.ParameterType.IsArray ? instance : mockInstance);
            }

            return mocks;
        }

        private static object GetInstructorInjectedType(IEnumerable<object> constructorDependencys, ParameterInfo parameter)
        {
            var firstOrDefault = constructorDependencys.FirstOrDefault(dep => parameter.ParameterType.IsAssignableFrom(dep.GetType()));

            return firstOrDefault;
        }
    }
}
